#include "Inf_ES8311.h"

#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
#include "esp_idf_version.h"
#include "soc/soc_caps.h"
#include "unity.h"

#define I2S_MAX_KEEP SOC_I2S_NUM

// I2S音频输出通道句柄，用于控制扬声器的音频数据传输出
static i2s_chan_handle_t speaker_handle;
// I2S通道句柄 (全局变量) I2S接收通道句柄 音频输入
static i2s_chan_handle_t mic_handle;
// 音频设备句柄 (全局变量)
static esp_codec_dev_handle_t codec_dev;

typedef struct {
  i2s_chan_handle_t tx_handle;
  i2s_chan_handle_t rx_handle;
} i2s_keep_t;

/**
 * @brief 初始化I2C
 *
 * @return int
 */
int Inf_ES8311_I2C_Init(void) {
  i2c_config_t i2c_cfg = {
      .mode = I2C_MODE_MASTER,
      .sda_pullup_en = GPIO_PULLUP_ENABLE,
      .scl_pullup_en = GPIO_PULLUP_ENABLE,
      .master.clk_speed = 100000,
      .sda_io_num = I2C_SDA_PIN,
      .scl_io_num = I2C_SCL_PIN,
  };
  i2c_param_config(I2C_NUM_0, &i2c_cfg);
  int re = i2c_driver_install(I2C_NUM_0, i2c_cfg.mode, 0, 0, 0);
  return re;
}

/**
 * @brief 初始化I2S
 *
 * @param port
 * @return int
 */
esp_err_t Inf_ES8311_I2S_Init(void) {

  // 配置I2S通道
  i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
  i2s_std_config_t std_cfg = {
      // 通过采样率配置时钟(保证工作时钟为采样率的整数倍)
      .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
      // 位深,双通道
      .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_MONO),
      .gpio_cfg =
          {
              .mclk = I2S_MCK_PIN,
              .bclk = I2S_BCK_PIN,
              .ws = I2S_DATA_WS_PIN,
              .dout = I2S_DATA_OUT_PIN,
              .din = I2S_DATA_IN_PIN,
          },
  };
  esp_err_t ret;
  // 配置双通道
  ret = i2s_new_channel(&chan_cfg, &speaker_handle, &mic_handle);

  // 初始化发送通道为标准模式
  ret = i2s_channel_init_std_mode(speaker_handle, &std_cfg);
  // 初始化接收通道为标准模式
  ret = i2s_channel_init_std_mode(mic_handle, &std_cfg);

  ret = i2s_channel_enable(speaker_handle);
  ret = i2s_channel_enable(mic_handle);
  return ret;
}

/**
 * @brief 反初始化I2S
 *
 * @param port
 */
int Inf_ES8311_I2S_Reverse_Init(void) {
  i2s_channel_disable(speaker_handle);
  i2s_channel_disable(mic_handle);
  i2s_del_channel(speaker_handle);
  i2s_del_channel(mic_handle);
  return 0;
}

/**
 * @description: 初始化设备
 * @return {*}
 */
void Inf_ES8311_DEV_Init(void) {
  // I2S通道配置
  audio_codec_i2s_cfg_t i2s_cfg = {
      .rx_handle = mic_handle,
      .tx_handle = speaker_handle,
  };
  // 数据接口
  const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);

  // I2C设备地址
  audio_codec_i2c_cfg_t i2c_cfg = {
      .addr = ES8311_CODEC_DEFAULT_ADDR,
  };
  // 控制接口
  const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);

  // GPIO操作接口
  const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();

  // ES8311所需要的配置信息
  es8311_codec_cfg_t es8311_cfg = {
      .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
      .ctrl_if = out_ctrl_if,
      .gpio_if = gpio_if,
      .pa_pin = BOARD_PA_PIN,
      .use_mclk = true,
      .digital_mic = false,
  };

  // 创建ES8311编解码器接口
  const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);

  // ES8311设备配置信息
  esp_codec_dev_cfg_t dev_cfg = {
      .codec_if = out_codec_if,
      .data_if = data_if,
      .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
  };

  // 根据配置信息创建设备
  codec_dev = esp_codec_dev_new(&dev_cfg);

  // 设置输出音量
  esp_codec_dev_set_out_vol(codec_dev, 60.0);

  // 设置输入增益
  esp_codec_dev_set_in_gain(codec_dev, 30.0);

  // 采样参数配置
  esp_codec_dev_sample_info_t fs = {
      .sample_rate = 16000,
      .channel = 1,
      .bits_per_sample = 16,
      .mclk_multiple = 256,
  };
  // 设备使能
  esp_codec_dev_open(codec_dev, &fs);
}

void Inf_ES8311_Init(void) {
  // 初始化I2C
  Inf_ES8311_I2C_Init();

  // 初始化I2S
  Inf_ES8311_I2S_Init();

  // 初始化ES8311设备
  Inf_ES8311_DEV_Init();
}

/**
 * @description: 发送数据到扬声器
 * @return {*}
 */
void Inf_ES8311_SendData(char *data, int len) {
  if (codec_dev != NULL && data != NULL && len > 0) {
    esp_codec_dev_write(codec_dev, data, len);
  }
}

/**
 * @description: 从麦克风读取数据
 * @return {*}
 */
void Inf_ES8311_ReceData(char *data, int len) {
  if (codec_dev != NULL && len > 0) {
    esp_codec_dev_read(codec_dev, data, len);
  }
}